#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#define BLOCK_SIZE 32

typedef struct {
	float red,blue,green;
}PPMPixel;

typedef struct {
	unsigned int height, width, maxValue;
	char magicNumber[3];
	// char comments[100];
}PPMHeader;

typedef struct {
	PPMHeader Header;
	PPMPixel **pixel;
}PPMImage;

clock_t begin,end;

__global__ void apply_smoothing_kernel(const PPMImage *d_src, PPMImage *d_dest){

    float sumred=0, sumblue=0, sumgreen=0;

    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    for (int k = row-2; k < row+3; row++){
        for (int l = col-2; l < col+3; col++){
            if(k > 0 && l > 0 && k < d_src->Header.height && l < d_src->Header.width){
                sumred += (d_src->pixel[k][l].red);
                sumgreen += (d_src->pixel[k][l].green);
                sumblue += (d_src->pixel[k][l].blue);}}}

    d_dest->pixel[row][col].red = sumred/25;
    d_dest->pixel[row][col].blue = sumblue/25;
    d_dest->pixel[row][col].green = sumgreen/25;}



void readHeader(FILE *in,PPMImage *img){
	char magicNumber[3];
	char comments[100];

	unsigned int width, height, maxValue;

	/* lendo magic number: P3 para PPM, P6 para PMM */
	fscanf(in,"%c",&magicNumber[0]);
	fscanf(in,"%c",&magicNumber[1]);
	magicNumber[2] = '\0';

	strcpy(img->Header.magicNumber,magicNumber);

	/* lendo largura e altura */
	fscanf(in,"%d %d",&width,&height);
	img->Header.width = width;
	img->Header.height = height;
	// cout << img->Header.width << "x" << img->Header.height << endl;

	/* alocando imagem PPM (preto) */
	img->pixel = (PPMPixel **) calloc (img->Header.height, sizeof(PPMPixel*));
	for (int i = 0; i < img->Header.height; i++){
		img->pixel[i] = (PPMPixel *) calloc (img->Header.width, sizeof(PPMPixel));
	}

	/* lendo valor maximo */
	fscanf(in,"%d",&maxValue);
	img->Header.maxValue = maxValue;
	// cout << maxValue << endl;

	return ;
}

void readImage(FILE *in, PPMImage *dest){
	/*code*/

	unsigned int height;
	unsigned int width;
	PPMPixel aux;


	for (int i = 0; i < dest->Header.height; i++){
		for (int j = 0; j < dest->Header.width; j++){

			/*read image*/
			fscanf(in,"%f",&dest->pixel[i][j].red);
			fscanf(in,"%f",&dest->pixel[i][j].green);
			fscanf(in,"%f",&dest->pixel[i][j].blue);
		}
	}

	return;
}


void writeImage(PPMImage *src, FILE *out){

	out = fopen("output.ppm","wb");
	if(out == NULL){
		printf("Cannot be opened\n");
		exit(1);
	}

	fprintf(out, "%s\n", src->Header.magicNumber);

	// fprintf(out, "%s\n", src->Header.comments);
	fprintf(out, "%d %d\n", src->Header.width, src->Header.height);
	fprintf(out, "%d\n", src->Header.maxValue);

	for (int i = 0; i < src->Header.height; i++){
		for (int j = 0; j < src->Header.width; j++){

			fprintf(out, "%d ", (int)src->pixel[i][j].red);
			fprintf(out, "%d ", (int)src->pixel[i][j].green);
			fprintf(out, "%d ", (int)src->pixel[i][j].blue);
		}
			fprintf(out, "\n");
	}

	return ;
}





int main(int argc, char **argv){

	PPMImage *img;
	PPMImage *img_out;
	PPMImage *dest_image, *src_image;
	FILE *input,*output;



	img = (PPMImage*) malloc (sizeof(PPMImage));
	img_out = (PPMImage*) malloc (sizeof(PPMImage));

	input = fopen(argv[1],"rb");
	if(input == NULL){
		printf("nao abriu\n");
		return -1;
	}

	printf("Reading Header....\n");
	readHeader(input,img);

	printf("Reading Image....\n");
	readImage(input,img);

	cudaMalloc(&dest_image, sizeof(PPMImage));
	cudaMalloc(&src_image, sizeof(PPMImage));

	img_out->Header = img->Header;
	img_out->pixel = (PPMPixel **) calloc (img->Header.height, sizeof(PPMPixel*));
	for (int i = 0; i < img->Header.height; i++){
		img_out->pixel[i] = (PPMPixel *) calloc (img->Header.width, sizeof(PPMPixel));
	}


	cudaMemcpy(dest_image, img_out, sizeof(PPMImage), cudaMemcpyHostToDevice);
	cudaMemcpy(src_image, img, sizeof(PPMImage), cudaMemcpyHostToDevice);

	dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE);
  dim3 dimGrid(ceil(img->Header.width / dimBlock.x), ceil(img->Header.height / dimBlock.y));

  	begin = clock();

	apply_smoothing_kernel<<<dimBlock, dimGrid>>>(src_image, dest_image);

	cudaMemcpy(img_out, dest_image, sizeof(PPMImage), cudaMemcpyDeviceToHost);

	end = clock();

    printf("writing image...\n");
    writeImage(img_out,output);

	printf("Done!\n");
	fclose(input);

	printf("tempo: %fs\n",(double) (end-begin)/CLOCKS_PER_SEC);

	return -1;
}
